package com.zx.idc.backend.gui.common;

import com.zx.idc.backend.gui.common.util.NetworkUtil;
import com.zx.idc.common.exception.GlobalException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import static com.zx.idc.backend.gui.common.ResultBuilder.success;

/**
 * 一些公共的控制器
 *
 * @author rxliuli
 * @date 2018/6/4
 */
@Controller
@RequestMapping("/common/")
public class CommonController extends BaseController {
    @Autowired
    private Environment environment;

    private String rootFilePath;

    @PostConstruct
    public void init() {
        this.rootFilePath = environment.getProperty("custom.rootFilePath");
        if (StringUtils.isEmpty(rootFilePath)) {
            log.error("Properties rootFilePath can not empty");
        }
    }

    /**
     * 重定向到某个页面
     */
    @GetMapping(path = "redirect")
    public String redirect(String pagePath) {
        return "redirect:" + pagePath;
    }

    /**
     * 转发到指定的视图
     */
    @GetMapping(path = "forward")
    public String forward(String pagePath) {
        return pagePath;
    }

    /**
     * 下载文件
     * 可以用作图片 url 路径
     *
     * @param filePath 相对路径
     */
    @GetMapping(path = "download")
    public void download(String filePath, HttpServletResponse response) {
        byte[] bytes = new byte[2048];
        int len;
        try (
            FileInputStream in = new FileInputStream(rootFilePath + filePath);
            ServletOutputStream out = response.getOutputStream()
        ) {
            File file = new File(rootFilePath + filePath);
            String fileName = new String(file.getName().getBytes("UTF-8"), "iso-8859-1");
            String disposition = String.format("attachment;filename=%s", fileName);
            response.setHeader("Content-Disposition", disposition);
            response.setContentType("application/octet-stream");
            while ((len = in.read(bytes)) != -1) {
                out.write(bytes, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            throw new GlobalException("获取文件下载链接错误", e);
        }
    }

    /**
     * 下载文件
     * 此接口不开放调用，和原生的下载区别不大
     *
     * @param filePath 相对下载路径
     * @return 返回的含有输入流资源的响应实体
     * @throws IOException 可能会找不到文件
     */
//    @GetMapping("downloadFile")
    public ResponseEntity<InputStreamResource> downloadFile(String filePath)
        throws IOException {
        FileSystemResource file = new FileSystemResource(rootFilePath + filePath);
        HttpHeaders headers = new HttpHeaders();
        headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
        headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getFilename()));
        headers.add("Pragma", "no-cache");
        headers.add("Expires", "0");
        return ResponseEntity
            .ok()
            .headers(headers)
            .contentLength(file.contentLength())
            .contentType(MediaType.parseMediaType("application/octet-stream"))
            .body(new InputStreamResource(file.getInputStream()));
    }

    /**
     * 上传单个文件
     *
     * @param file    要上传的单个文件
     * @param dirPath 文件相对路径
     * @return 上传成功后的文件相对路径
     */
    @PostMapping(path = "uploadFile")
    @ResponseBody
    public Result<String> uploadFile(MultipartFile file, String dirPath) {
        if (file == null || dirPath == null) {
            throw new GlobalException(400, "文件不存在或上传路径不存在");
        }
        if (file.isEmpty()) {
            return ResultBuilder.error("上传失败，文件不存在");
        }
        final GetLocalFile getLocalFile = new GetLocalFile(file, dirPath).invoke();
        final String relativelyPath = getLocalFile.getRelativelyPath();
        final File localFile = getLocalFile.getLocalFile();
        try (
            final FileOutputStream fos = new FileOutputStream(localFile);
            final BufferedOutputStream bos = new BufferedOutputStream(fos)
        ) {
            bos.write(file.getBytes());
            bos.flush();
            return success(relativelyPath);
        } catch (IOException e) {
            log.error("file upload failed ", e);
            return ResultBuilder.error("上传文件失败");
        }
    }

    /**
     * 上传多个文件
     *
     * @param files   多个文件列表
     * @param dirPath 上传文件的相对路径
     * @return 上传成功后的文件相对路径列表
     */
    @PostMapping(path = "uploadFiles")
    @ResponseBody
    public Result<List<String>> uploadFiles(List<MultipartFile> files, String dirPath) {
        final List<String> filePathList = new ArrayList<>();
        for (final MultipartFile file : files) {
            if (file.isEmpty()) {
                return ResultBuilder.error("文件不存在");
            } else {
                final GetLocalFile getLocalFile = new GetLocalFile(file, dirPath).invoke();
                final String relativelyPath = getLocalFile.getRelativelyPath();
                final File localFile = getLocalFile.getLocalFile();
                try (
                    final FileOutputStream fos = new FileOutputStream(localFile);
                    final BufferedOutputStream bos = new BufferedOutputStream(fos)
                ) {
                    bos.write(file.getBytes());
                    bos.flush();
                    filePathList.add(relativelyPath);
                } catch (Exception e) {
                    return ResultBuilder.error("文件上传错误");
                }

            }
        }
        return success(filePathList);
    }

    /**
     * 获取 ip 地址
     */
    @GetMapping("current-ip")
    @ResponseBody
    public Result<String> currentIp(HttpServletRequest request) {
        return success(NetworkUtil.getIpAddress(request));
    }

    /**
     * 根据上传文件和上传目录相对路径得到本地文件信息的内部私有类
     */
    private class GetLocalFile {
        private MultipartFile file;
        private String dirPath;
        private String relativelyPath;
        private File localFile;

        public GetLocalFile(MultipartFile file, String dirPath) {
            this.file = file;
            this.dirPath = dirPath;
        }

        public String getRelativelyPath() {
            return relativelyPath;
        }

        public File getLocalFile() {
            return localFile;
        }

        public GetLocalFile invoke() {
            //文件名
            String filename = file.getOriginalFilename();
            //文件扩展名
            final String suffix = filename.substring(filename.lastIndexOf('.'));
            //避免中文乱码问题
            relativelyPath = concatPath(dirPath) + UUID.randomUUID() + suffix;
            //获取真实的路径
            String realPath = rootFilePath + relativelyPath;
            //创建本地文件对象
            localFile = new File(realPath);
            if (!localFile.getParentFile().exists()) {
                localFile.getParentFile().mkdirs();
            }
            return this;
        }

        private String concatPath(String path) {
            String separator = "/";
            if (path == null || path.isEmpty()) {
                return separator;
            }
            final String start = path.startsWith(separator) ? "" : separator;
            final String end = path.endsWith(separator) ? "" : separator;
            return start + path + end;
        }
    }
}
